/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.webos.java.base.grid;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
import org.webos.java.base.Frame;
import org.webos.java.root.Log;

/**
 *
 * @author Kostya
 */
public class GridController <E extends GridControllerItem>{
    private List<E> items = new ArrayList<E>();
    private Frame frame = null;
    private boolean pager = false;
    private int pageSize = 20;
    private int pageStart = 0;
    private E selected = null;
    private boolean silent = false;
    private Comparator<E> comparator = null;

    public GridController(Frame aFrame, boolean aPager, int aPageSize) {
        this(aFrame);
        pager = aPager;
        pageSize = aPageSize;
    }

    public GridController(Frame aFrame) {
        frame = aFrame;
        frame.addHandler("setSelected", this);
        frame.addHandler("loadGrid", this);
    }

    private JSONObject getGridState(){
        JSONObject object = new JSONObject();
        object.putOpt("items", items.size());
        int page = (int) Math.floor(pageStart/pageSize);
        int pages = (int) Math.ceil((double)items.size()/(double)pageSize);
        if(pages==0)
            pages = 1;
        object.putOpt("page", page+1);
        object.putOpt("pages", pages);
        return object;
    }

    private void sendAdd(E aItem, int index){
        JSONObject object = aItem.getJSONObject();
//        Log.debug(this, "Send add", index, pageStart, index-pageStart, object.optString("text"));
        object.putOpt("index", index-pageStart);
        object.putOpt("state", getGridState());
        frame.sendMessage("gridItemAdded", object);
    }

    public void appendItem(E aItem){
        removeItem(aItem);
        if(comparator!=null){
            for (int i = 0; i < getItemCount(); i++) {
                E item = getItem(i);
                if(comparator.compare(aItem, item)<=0){
                    addItem(i, aItem);
                    return;
                }
            }
        }
        addItem(getItemCount(), aItem);
    }

    public void addItem(int index, E aItem){
//        Log.debug(this, "Add item to grid", index, aItem);
        if(index<items.size()){
            items.add(index, aItem);
        }else{
            index = items.size();
            items.add(aItem);
        }
        if(silent)
            return;
        if(pager && (index<pageStart || index>=pageStart+pageSize)){
            if(index<pageStart && pageStart<items.size()){
                if(items.size()>pageStart+pageSize){
                    sendRemove(items.get(pageStart+pageSize), pageStart+pageSize-1);
                }
                sendAdd(items.get(pageStart), pageStart);
            }
            return;
        }
        if(items.size()>pageStart+pageSize){
            sendRemove(items.get(pageStart+pageSize), pageStart+pageSize-1);
        }
        sendAdd(aItem, index);

    }

    private void sendRemove(E aItem, int index){
//        Log.debug(this, "Send remove", index, pageStart, index-pageStart);
        JSONObject object = aItem.getJSONObject();
        object.putOpt("index", index-pageStart);
        object.putOpt("state", getGridState());
        frame.sendMessage("gridItemRemoved", object);
    }

    public void removeItem(E aItem){
        int index = items.indexOf(aItem);
        if(index==-1)
            return;
        items.remove(index);
        if(silent)
            return;
        if(pager && (index<pageStart || index>=pageStart+pageSize)){
            if(index<pageStart && pageStart<items.size()){
                sendRemove(items.get(pageStart-1), pageStart);
                int indexToAdd = pageStart+pageSize-1;
                if(indexToAdd<items.size()-1){
                    sendAdd(items.get(indexToAdd), indexToAdd);
                }
            }
            return;
        }
        sendRemove(aItem, index);
        int indexToAdd = pageStart+pageSize-1;
        if(pager && indexToAdd<items.size()){
            sendAdd(items.get(indexToAdd), indexToAdd);
        }
    }

    public void setSelected(E aSelected){
        selected = aSelected;
        if(silent)
            return;
        if(selected!=null){
            int index = items.indexOf(selected);
            if(pager && (index<pageStart || index>=pageStart+pageSize)){
                return;
            }            
            JSONObject object = selected.getJSONObject();
            object.putOpt("index", index-pageStart);
            object.putOpt("state", getGridState());
            frame.sendMessage("gridItemSelected", object);            
        }else{
            JSONObject object = new JSONObject();
            object.putOpt("state", getGridState());
            frame.sendMessage("gridItemSelected", object);                        
        }

    }

    public E getSelected(){
        return selected;
    }

    public void sendGrid(){
        setSilent(false);
        frame.sendMessage("loadGrid", loadGrid(new JSONObject()));
    }

    public void sendGridState(){
        setSilent(false);
        frame.sendMessage("setGridState", getGridState());
    }

    //Remote
    public void selectionChanged(JSONObject aData){
        E item = getItem(aData.optString("id", ""));
        selected = item;
    }

    public void checkPageCount(){
        if(pager && pageStart>=items.size()){
            JSONObject object = new JSONObject();
            object.putOpt("direction", "last");
            JSONObject result = loadGrid(object);
            if(result!=null)
                frame.sendMessage("loadGrid", result);
        }
    }

    //Remote
    public JSONObject loadGrid(JSONObject aData){
        int pages = (int) Math.ceil((double)items.size()/(double)pageSize);
        if(pages<1)
            pages = 1;
        int page = (int) Math.floor(pageStart/pageSize);
        if(page>=pages)
            page = pages-1;
        String direction = aData.optString("direction", "none");
//        Log.debug(this, "loadGrid start", page, pages, direction);
        if("left".equalsIgnoreCase(direction) && page>0){
            page--;
        }
        if("first".equalsIgnoreCase(direction)){
            page = 0;
        }
        if("right".equalsIgnoreCase(direction)){
            if(page<pages-1){
                page++;
            }else
                page = pages-1;
        }
        if("last".equalsIgnoreCase(direction)){
            page = pages-1;
        }
        pageStart = pageSize*page;
//        Log.debug(this, "loadGrid end", page, pages, pageStart);

        if(pageStart<0)
            pageStart = 0;
        if(silent)
            return null;
        int size = items.size();
        if(pager)
            size = pageStart+pageSize;
        if(size>items.size())
            size = items.size();
        JSONArray arr = new JSONArray();
        for (int i = pageStart; i < size; i++) {
            E item = items.get(i);
            JSONObject object = item.getJSONObject();
            arr.put(object);
        }
        JSONObject result = new JSONObject();
        result.putOpt("items", arr);
        result.putOpt("state", getGridState());
        result.putOpt("selected", selected!=null?selected.getId():"");
        return result;
    }

    public E getItem(String aID){
        for (GridControllerItem itm : items) {
            if(itm.getId().equals(aID))
                return (E) itm;
        }
        return null;
    }

    /**
     * @return the silent
     */
    public boolean isSilent() {
        return silent;
    }

    /**
     * @param silent the silent to set
     */
    public void setSilent(boolean silent) {
        this.silent = silent;
    }

    public int getItemCount(){
        return items.size();
    }

    public E getItem(int index){
        return items.get(index);
    }

    public void clear(){
        setSilent(true);
        pageStart = 0;
        items.clear();
    }

    /**
     * @param comparator the comparator to set
     */
    public void setComparator(Comparator<E> comparator) {
        this.comparator = comparator;
    }

}
